﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Effects.Effects.GuiParts;

namespace Effects.Effects
{
    /// <summary>
    ///  Three band active equalizer using IIR 4th order filters.
    /// </summary>
    public class EQ:Effect
    {

        #region controls
        // user controlled parameters 

        #region footswitch

        /// <summary>
        ///  Footswitch for by passing effect.
        /// </summary>
        private Footswitch footswitch;

        /// <summary>
        ///  Led indicating On/Off state.
        /// </summary>
        private LED led;

        /// <summary>
        ///  Synchornizer for footswitch and led.
        /// </summary>
        private FootswitchLedSynchronizer synchronizer;


        /// <summary>
        ///  Key for footswitch settings.
        /// </summary>
        private int footswitchKey = 0;

        #endregion

        #region bass
        
        /// <summary>
        ///  Potentiometer controlling bass gain.
        /// </summary>
        private Pot Bass;

        /// <summary>
        ///  Gain of the bass peak filter in decibels.
        /// </summary>
        private double bassGainDB;

        /// <summary>
        ///  gain of the bass peak filter.
        /// </summary>
        private double bassGain;

        /// <summary>
        ///  Bass peak filter
        /// </summary>
        private Filters.FourthOrderFilter bassFilter;

        /// <summary>
        ///  Upper frequancy of the bass peak.
        /// </summary>
        private int bassFrequencyUp;

        /// <summary>
        ///  Lower frequancy of the bass peak.
        /// </summary>
        private int bassFrequencyLow;

        /// <summary>
        ///  Key for the bass parameter.
        /// </summary>
        private const int bassKey = 1;

        #endregion

        #region middle

        /// <summary>
        ///  Potentiometer controlling middle gain.
        /// </summary>
        private Pot Middle;

        /// <summary>
        ///  Gain of the middle peak filter in decibels.
        /// </summary>
        private double middleGainDB;

        /// <summary>
        ///  Gain of the middle peak filter.
        /// </summary>
        private double middleGain;

        /// <summary>
        ///  Bass peak filter
        /// </summary>
        private Filters.FourthOrderFilter middleFilter;

        /// <summary>
        ///  Upper frequancy of the middle peak.
        /// </summary>
        private int middleFrequencyUp;

        /// <summary>
        ///  Lower frequency of the middle peak.
        /// </summary>
        private int midddleFrequencyLow;

        /// <summary>
        ///  Key for the middle parameter
        /// </summary>
        private const int middleKey = 2;

        #endregion

        #region treble

        /// <summary>
        ///  Potentiometer controlling treble gain.
        /// </summary>
        private Pot Treble;

        /// <summary>
        ///  Gain of the treble peak filter in decibels.
        /// </summary>
        private double trebleGainDB;

        /// <summary>
        ///  Gain of the treble peak filter.
        /// </summary>
        private double trebleGain;

        /// <summary>
        ///  treble peak filter
        /// </summary>
        private Filters.FourthOrderFilter trebleFilter;

        /// <summary>
        ///  Lower frequancy of the treble peak.
        /// </summary>
        private int trebleFrequencyLow;

        /// <summary>
        ///  Upper frequancy of the treble peak.
        /// </summary>
        private int trebleFrequencyUp;

        /// <summary>
        ///  Key for the treble parameter.
        /// </summary>
        private const int trebleKey = 3;

        #endregion

        #endregion

        #region constructor 
        // constructor and initialization

        /// <summary>
        ///  Creates new instance of EQ
        /// </summary>
        public EQ()
        {
            this.BackgroundImage = System.Drawing.Image.FromStream(new System.IO.MemoryStream(global::Effects.Recources.EffectBackgorunds.Equalizer));
            this.Size = BackgroundImage.Size;
            
           
            InitializeComponents();
            InitializeFootswitch();
            InitializeFilters();
        }

        /// <summary>
        ///  Initializes potentiometers
        /// </summary>
        private void InitializeComponents()
        {
            bassFrequencyLow = 20;
            bassFrequencyUp = 200;

            Bass = new Pot();
            Bass.MaxValue = 6;
            Bass.MinValue = -6;
            Bass.Knob=PotKnob.SpiderKnobSmall;
            Bass.ValueChanged += new PotValueChangedEventHandler(this.Bass_moved);
            Bass.Location = new System.Drawing.Point(10, 45);
            this.Controls.Add(Bass);
            Bass.Reset();

            midddleFrequencyLow = 200;
            //midddleFrequencyLow = 400;
            middleFrequencyUp = 2000;


            Middle = new Pot();
            Middle.MaxValue = 6;
            Middle.MinValue = -6;
            Middle.Knob = PotKnob.SpiderKnobSmall;
            Middle.ValueChanged += new PotValueChangedEventHandler(this.Middle_moved);
            Middle.Location = new System.Drawing.Point(75,100);
            this.Controls.Add(Middle);
            Middle.Reset();

            //trebleFrequency = 3000;
            //trebleQ = (double) 1F / Math.Sqrt(2);
            trebleFrequencyLow = 2000;
            //trebleFrequencyLow = 5000;
            trebleFrequencyUp = 20480;

            Treble = new Pot();
            Treble.MaxValue = 6;
            Treble.MinValue = -6;
            Treble.Knob = PotKnob.SpiderKnobSmall;
            Treble.ValueChanged += new PotValueChangedEventHandler(this.Treble_moved);
            Treble.Location = new System.Drawing.Point(140, 45);
            this.Controls.Add(Treble);
            Treble.Reset();
        }

        /// <summary>
        ///  Initializes filters used in equalizer.
        /// </summary>
        private void InitializeFilters()
        {
            bassFilter = Filters.FourthOrderFilter.CreateBandPass(bassFrequencyLow, bassFrequencyUp, (int)sampleRate);
            middleFilter = Filters.FourthOrderFilter.CreateBandPass(midddleFrequencyLow, middleFrequencyUp, (int)sampleRate);
            trebleFilter = Filters.FourthOrderFilter.CreateBandPass(trebleFrequencyLow, trebleFrequencyUp, (int)sampleRate);
        }

        /// <summary>
        ///  Iniializes footswitch and led.
        /// </summary>
        private void InitializeFootswitch()
        {
            // footswitch
            footswitch = new Footswitch();
            switchedOn = true;
            footswitch.On = true;
            footswitch.Switched += new FootswitchSwitchedEventHandler(this.FootswitchSwitched);
            footswitch.Location = new System.Drawing.Point(145, 130);
            Controls.Add(footswitch);

            // led
            led = new LED();
            led.On = true;
            led.Location = new System.Drawing.Point(90, 30);
            Controls.Add(led);

            // synchronization
            synchronizer = new FootswitchLedSynchronizer(footswitch, led);
        }

        #endregion

        #region effects_properties

        /// <summary>
        ///  Time interval between adjacent samples in seconds
        /// </summary>
        private double step;

        /// <summary>
        ///  Gets or sets the sampling rate
        /// </summary>
        public override uint SampleRate
        {
            get{ return base.SampleRate; }
            set
            {
                sampleRate = value;
                step = 1F / sampleRate;
                InitializeFilters();
            }
        }

        /// <summary>
        ///  Gets the name of the effect.
        /// </summary>
        public override string Name
        {
            get{ return "EQ v 1.0"; }
        }

        #endregion
       
        #region main_work
        // main method

        /// <summary>
        ///  Applies equalization on given input signal.
        /// </summary>
        /// <param name="data"> Array containing part of input signal. </param>
        /// <returns> Processed ouput signal. </returns>
        public override double[] effect(double[] data)
        {
           
            double[] outData = new double[data.Length];

            for (int i = 0; i < data.Length; i++)
            {
                data[i] *= 0.5;
                outData[i] = data[i] + bassGain * bassFilter.Step(data[i]) + middleGain * middleFilter.Step(data[i]) + trebleGain *  trebleFilter.Step(data[i]);
            }


            return outData;
        }

        #endregion

        #region controls_event_handlers
        // handling events from controls

        /// <summary>
        ///  <see cref="EQ.Bass"/> pot event handler
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        private void Bass_moved(object sender, PotValueChangedEventArgs e)
        {
            bassGainDB = e.NewValueInt;
            bassGain = Math.Pow(10, bassGainDB / 20) - 1;

            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(bassKey,bassGainDB);
            if(SettingsChanged != null)
                SettingsChanged(this,changeArgs);
        }

        /// <summary>
        ///  <see cref="EQ.Middle"/> event handler
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        private void Middle_moved(object sender, PotValueChangedEventArgs e)
        {
            middleGainDB = e.NewValue;
            middleGain = Math.Pow(10, middleGainDB / 20) - 1;

            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(middleKey,middleGainDB);
            if(SettingsChanged != null)
                SettingsChanged(this,changeArgs);
        }

        /// <summary>
        ///  <see cref="EQ.Treble"/> event handler
        /// </summary>
        /// <param name="sender"> Object that has raised the event </param>
        /// <param name="e"> Event arguments </param>
        private void Treble_moved(object sender, PotValueChangedEventArgs e)
        {
            trebleGainDB = e.NewValue;
            trebleGain = Math.Pow(10, trebleGainDB / 20) - 1;

            EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(trebleKey, trebleGainDB);
            if (SettingsChanged != null)
                SettingsChanged(this, changeArgs);
        }

        /// <summary>
        ///  Footswitch.Switched event handler. According to the event arguments signal bypasses or runs through the effect.
        /// </summary>
        /// <param name="sender"> Object that has raised the event. </param>
        /// <param name="args"> Event Arguments </param>
        private void FootswitchSwitched(object sender, FootswitchSwitchedEventArgs args)
        {
            this.switchedOn = args.On;

            // raise the event
            if (this.SettingsChanged != null)
            {
                EffectSettingsChangedEventArgs changeArgs = new EffectSettingsChangedEventArgs(footswitchKey, switchedOn);
                this.SettingsChanged(this, changeArgs);
            }
        }

        #endregion

        #region settings
        // getting and setting the settings of EQ

        /// <summary>
        ///  Raised when any parameter is changed
        /// </summary>
        public override event EffectSettingsChangedDelegate SettingsChanged;

        /// <summary>
        ///  Gets all current settings of the effect.
        /// </summary>
        /// <returns> All current seetings </returns>
        public override object[] GetAllSettings()
        {
            object[] settings = new object[4];

            settings[0] = switchedOn;
            settings[1] = bassGainDB;
            settings[2] = middleGainDB;
            settings[3] = trebleGainDB;

            return settings;
        }

        /// <summary>
        ///  Sets all settings.
        /// </summary>
        /// <param name="settings"> All settings of the effect. This argument should be output of GetAllSettings method that has been called previously. </param>
        public override void SetAllSettings(object[] settings)
        {
            switchedOn = (bool)settings[0];
            synchronizer.SetValue(switchedOn);

            bassGainDB = (double)settings[1];
            bassGain = Math.Pow(10, bassGainDB / 20) - 1;
            Bass.SetValue(bassGainDB);

            middleGainDB = (double)settings[2];
            middleGain = Math.Pow(10, middleGainDB / 20) - 1;
            Middle.SetValue(middleGainDB);

            trebleGainDB = (double)settings[3];
            trebleGain = Math.Pow(10, trebleGainDB / 20) - 1;
            Treble.SetValue(trebleGainDB);
        }

        /// <summary>
        ///  Sets one settings
        /// </summary>
        /// <param name="e"> Setting change to be done </param>
        public override void SetSetting(EffectSettingsChangedEventArgs e)
        {
            int key = (int) e.Key;

            if(key == footswitchKey)
            {
                switchedOn = (bool) e.Value;
                synchronizer.SetValue(switchedOn);
                return;
            }

            if(key == bassKey)
            {
                bassGainDB = (double) e.Value;
                bassGain = Math.Pow(10, bassGainDB / 20) - 1;
                Bass.SetValue(bassGainDB);
                return;
            }

            if(key == middleKey)
            {
                middleGainDB = (double) e.Value;
                middleGain = Math.Pow(10, middleGainDB / 20) - 1;
                Middle.SetValue(middleGainDB);
                return;
            }

            if(key == trebleKey)
            {
                trebleGainDB = (double) e.Value;
                trebleGain = Math.Pow(10, trebleGainDB / 20) - 1;
                Treble.SetValue(trebleGainDB);
                return;
            }
        }

        /// <summary>
        ///  Gets parameter description.
        /// </summary>
        /// <returns> EffectParam array descrobing effect's parameters. </returns>
        public override EffectParam[]  GetParameters()
        {
 	        EffectParam[] parameters = new EffectParam[4];
            
            ListItem[] footswitchState = new ListItem[2];
            footswitchState[0] = new ListItem("Turn on", true);
            footswitchState[1] = new ListItem("Turn off", false);
            parameters[0] = new EffectParam(footswitchKey, "ON/OFF", footswitchState);

            parameters[1] = new EffectParam(bassKey,"Bass",-6F,6F);
            parameters[2] = new EffectParam(middleKey,"Middle",-6F,6F);
            parameters[3] = new EffectParam(trebleKey,"Treble",-6F,6F);

            return parameters;
        }

        #endregion
    }
}
